<?php
namespace App\Common\Lib\Cache\KeyDB;

use App\Common as com;
use App\Common\Lib\Cache\KeyDB\_Cache as name_Cache;
use App\Common\Lib\Cache\KeyDB as nameKeyDB;
use Hyperf\Redis\Redis as nameRedis;

/**
 * 数据缓存层
 * @property nameKeyDB\DataStore $dataStore
 */
class _Cache extends _Base {

	/**
	 * @return nameRedis
	 */
	public function goRedis(  ):nameRedis {
		return $this->goPool( 'goRedis', function(){
			return com\Lib\Hyperf\Container::getRedis();
		} );
	}

	/**
	 * 设置缓存数据
	 * @param mixed $value 存储数据
	 * @param int|null $expire 有效时间，单位：秒。[null]永久 [int]必须大于0
	 * @return bool [true][必返]
	 * @throws
	 */
	public function set( $value, ?int $expire ):bool {
		// ...
		if( $expire !== null && $expire <= 0 ){
			throw com\Lib\Err\Exception::makeOfNew( 'expire需大于0', com\Lib\Err\Code::$sysFunParamError );
		}
		//
		if( $expire === null ){
			if( $this->goRedis()->set( $this->getKeyName(), $this->dataStore->main->pack( $value ) ) !== true ){
				throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysSqlSavaDataError );
			}
		}else{
			if( $this->goRedis()->setex( $this->getKeyName(), $expire, $this->dataStore->main->pack( $value ) ) !== true ){
				throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysSqlSavaDataError );
			}
		}
		//
		return true;
	}

	/**
	 * 删除缓存
	 * @return bool [true][必返]
	 */
	public function del(  ):bool {
		$this->goRedis()->del( $this->getKeyName() );
		//
		return true;
	}

	/**
	 * 获取缓存数据
	 * @param mixed $default 默认值。[false][默认]
	 * @return mixed 数据
	 * @throws
	 */
	public function get( $default = false ) {
		$value = $this->goRedis()->get( $this->getKeyName() );
		if( $value === false ){
			return $default;
		}
		//
		return $value;
	}

	/**
	 * 判断缓存是否存在
	 * @return boolean 是否存在 [true]存在 [false]不存在
	 */
	public function has(  ):bool {
		return $this->goRedis()->exists( $this->getKeyName() );
	}

	/**
	 * 设置ttl
	 * @param int|null $expire 有效时间。[null]永久
	 * @return bool
	 */
	public function setTtl( ?int $expire ):bool {
		if( $expire === null ){
			// 永久
			$cacheData = $this->get();
			if( false === $cacheData ) {
				throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataNotExist );
			}
			return $this->set( $cacheData, $expire );
		}else{
			return $this->goRedis()->expire( $this->getKeyName(), $expire );
		}
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @return int 剩余过期时间。[-2]key不存在时 [-1]key 存在但没有设置剩余生存时间 [>=0]剩余过期时间
	 */
	public function ttl():int {
		return $this->goRedis()->ttl( $this->getKeyName() );
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @return int|null 剩余过期时间。 [null]永久 [>=0]剩余过期时间
	 */
	public function ttlOfExpireOrFail():?int {
		$ttl = $this->ttl();
		if( $ttl >= 0 ){
			return $ttl;
		}else if( $ttl === -1 ){
			return null;
		}else{
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysDataError );
		}
	}

	/**
	 * 命令以秒为单位返回 key 的剩余过期时间。
	 * @param  int $default 默认返回过期时间
	 * @return int|null 剩余过期时间。 [null]永久 [>=0]剩余过期时间
	 */
	public function ttlOfExpireOrDefault( int $default ):?int {
		$ttl = $this->ttl();
		if( $ttl >= 0 ){
			return $ttl;
		}else if( $ttl === -1 ){
			return null;
		}else{
			return $default;
		}
	}

	/**
	 * 自增缓存（针对数值缓存）
	 * @param int $step 步长
	 * @param int|null $expire 有效时间，单位：秒。[null]永久 [int]必须大于0
	 * @return int 修改后的值
	 */
	public function incX( int $step, ?int $expire ){
		$value = $this->goRedis()->incrby( $this->getKeyName(), $step );
		if( $value === 1 && $expire > 0 ){
			$this->setTtl( $expire );
		}
		//
		return $value;
	}

	/**
	 * 自减缓存（针对数值缓存）
	 * @param int $step 步长
	 * @param int|null $expire 有效时间，单位：秒。[null]永久 [int]必须大于0
	 * @return int 修改后的值
	 */
	public function decX( $step, ?int $expire ){
		$value = $this->goRedis()->decrBy( $this->getKeyName(), $step );
		if( $value === 1 && $expire > 0 ){
			$this->setTtl( $expire );
		}
		//
		return $value;
	}

}
